/** @jsxImportSource @emotion/react */
import {jsx,css} from "@emotion/react";
import * as React from "react";
import {CSSProperties} from "react";
import {
    useGestureResponder,
    StateType,
    Callbacks,
    ResponderEvent
} from "react-gesture-responder";
import {  useSpring, SpringConfig } from "react-spring";
import { useSprings, animated } from '@react-spring/web';
//类型问题
// @ts-ignore
import {useDrag, useGesture} from '@use-gesture/react';
// import clamp from 'lodash.clamp'
// import { useMeasure } from "./Hooks/use-measure";
// import { usePrevious } from "./Hooks/previous";
// import {useScrollLock} from "./Hooks/use-scroll-lock";
import {  usePrevious,
    useTheme, Text, LayerRefComp,Touchable,
} from "customize-easy-ui-component";
import {useMeasure} from "customize-easy-ui-component/esm/Hooks/use-measure";
import {useScrollLock} from "customize-easy-ui-component/esm/Hooks/use-scroll-lock";
import {Vector2} from "@use-gesture/core/dist/declarations/src/types/utils";



//很容易触发框架层的拉动！？



/**
 * ReactPager
 * Provide views that can be swiped left or right (with touch devices).
 * 旧的有interface PagerProps extends React.HTMLAttributes<HTMLDivElement> ； children类型冲突；
 */
export interface PagerProps  {
    id?: string | undefined;
    //代表是当前显示哪一个儿子; onRequestChange配合执行，上级组件决定最终 value：index 该显示的儿子序号。
    value: number;
    //回调触发：切换到哪一个儿子去了
    //【注意】onRequestChange 必须把 value 参数值进行修改，否则无法真的切换。支持 异步的 懒惰加载组件数据。
    onRequestChange: (value: number) => void;

    //允许很长儿子队列只需要加载一部分儿子。
    lazyLoad?: boolean;      //懒加载总开关: 允许某些儿子mount实际时间滞后。
    //允许部分加载机制： 依据suggestedIndex来回调设置应当同时给与加载的那些兄弟，不是都是拖延加载的做法;
    onSetLazy?: (suggestedIndex: number) => number[];
    /* 拉不动了： 转交给上一级的轮播台机会拉动 ；
       beyondX 拉拽X方向已经超限制了：=-1 表示X轴左边超出  =1 表示X轴右边超出。  beyondY 高度方向的超出。
    * */
    onNestDragEnd?: (direction: Vector2) => boolean;

    //不能使用这样 paneStyle?: SerializedStyles;定义； 没法注入 style:{};
    paneStyle?: CSSProperties | undefined;

    children: Array<React.ReactNode>;
    //children: Array<React.ReactNode | CallbackProps>;
    //style: 隐藏 在HTMLAttributes里面
    style?: CSSProperties | undefined;
    //要不要允许手势操作处理，允许拉拽？
    noBind?: boolean;
}

//向上级组件暴露的操作接口
export interface PagerHandles {
    focus(i?: number): void;
}

//文档例子：https://codesandbox.io/s/github/pmndrs/use-gesture/tree/main/demo/src/sandboxes/draggable-list
export const GesturePage  = React.forwardRef(
    (
        {
            children,
            id,
            value: index,
            onRequestChange,
            lazyLoad = false,
            onSetLazy,
            onNestDragEnd,
            style,
            paneStyle,
            noBind=false,
            ...other
        } : PagerProps,
        ref
  ) =>  {
    const indexRef = React.useRef(index)
    const containerRef = React.useRef(null);      //绑定 拉拽的第一层Div;
    const { bounds } = useMeasure(containerRef);
    const { width } = bounds
    //已经加载的部分： 多个儿子，不一定都加载mount完毕; 轮播切换不会卸载。 [index]默认只能加载一个;
    const [loaded, setLoaded] = React.useState(
    () => new Set(onSetLazy ? onSetLazy(index) : [index])
     );
    //当前已经加载的儿子映射DOM;
    const childrenRefs = React.useRef<Map<number, HTMLDivElement | null>>(
        new Map()
    );
    const previousIndex = usePrevious(index);
    const shouldFocusRef = React.useRef<number | null>(index || null);

    React.useEffect(() => {
        if (typeof previousIndex === "number" && previousIndex !== index) {
            shouldFocusRef.current = index;
        } else {
            shouldFocusRef.current = null;
        }
    }, [previousIndex, index]);
     //直接定位滚到某个儿子吗？
    function focusByIndex(i: number) {
        const el = childrenRefs.current.get(i);
        if (el) {
            el.focus();
        }
    }
    // expose an imperative focus function which focuses the currently active index
    //上级组件可能使用本接口直接操作 focusByIndex切换到某一个儿子， 跳过轮播滑动:也是可能切换轮播台的。
    React.useImperativeHandle(ref, () => ({
        focus: (i?: number) => {
            focusByIndex(i || index);
        }
    }));

    const renderableChildren = children.filter(child => child !== null);
    const childCount = renderableChildren.length;

    function isValidNextIndex(index: number) {
        return index >= 0  && index <= (childCount - 1);
    }

    /** 跟踪设置应该加载的儿子;
     * We keep a set of indexes that should
     * be loaded for lazy loading.
     * @param index:, 应该加载的儿子
     */
    function addIndexToLoaded(index: number) {
        if (!isValidNextIndex(index)) {
            return;
        }
        let indexes: number | number[] = index;

        // allow the user to customize which indexes to load
        if (onSetLazy) {
            indexes = onSetLazy(index);
        }

        const indexesArray = Array.isArray(indexes) ? indexes : [indexes];
        const next = new Set(loaded);
        //重新洗牌 next= 旧的已经加载儿子们；
        indexesArray.forEach(i => {
            // don't set items which are already loaded or are invalid
            if (loaded.has(i) || !isValidNextIndex(index)) {
                return;
            }
            next.add(i);
        });
        setLoaded(next);
    }

    function doDragEnd(state : any) {
        const { active, movement: [mx,my], direction: [xDir,yDir] , cancel,
                            swipe:[swpx,], last,  canceled, } = state;
        // console.log("拉动End",state,"active",active,"号序",indexRef.current,"canceled:",canceled,"快滑",swpx);
        if(!active && last && !canceled && (Math.abs(swpx)>0 || (Math.abs(mx)>=width*0.03 && Math.abs(mx)>Math.abs(my) ) ) ) {
            let Old=indexRef.current;
            let finalDir= xDir!==0? xDir : mx>0? 1 : -1;
            if(xDir===0  && yDir!==0)   finalDir=0;      //允许水平方向滑动
            if(finalDir!==0){
                const idxNew= indexRef.current + (finalDir>0 ? -1 : 1);
                const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;

                console.log("拉动End Old",Old,"objIndex=",objIndex);
                if(objIndex===Old){
                    cancel();
                    onNestDragEnd && onNestDragEnd([finalDir,0]);
                }
                else {
                    //滑动未到位就该触发懒加载的准备机制。
                    addIndexToLoaded(objIndex);
                    indexRef.current = objIndex;
                    onRequestChange(objIndex);
                    cancel();
                }
            }
        }
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        });
    }

    const [anmprops, api] = useSprings(childCount, i => ({
            x: i * width,
            scale: 1,
            display: 'block',
    }));

    const bind = useGesture(
        {
            onDragEnd: (state) => doDragEnd(state),
            onPointerMove:  ({ event, ...sharedState }) =>{
                if(event.pointerType!=="mouse")
                     event.stopPropagation();
            },
        },
        {
            drag: {
                filterTaps: true,
            },
        }
    );

    React.useEffect(() => {
        loaded.add(index);
        indexRef.current=index;
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1)
                return { display: 'none' }
            const x = (i - indexRef.current) * width ;
            const scale =  1;
            return { x, scale, display: 'block' }
        })
    }, [index, width, api, loaded]);

   return (
        <div className="Pager"  key={id}
             ref={containerRef}
             style={{
                overflow: "hidden",
                width: "100%",
                position: "relative",
                height: "100%",
                ...style,
               }}
           {...other}
        >
            {anmprops.map(({ x, display, scale }, i) => {
                const child= renderableChildren[i];
                //传递给子页面： ref是一个动作函数(this) ？绑定时刻会执行ref函数: 非当前轮播儿子组件的直接"aria-hidden"隐藏?。
                const sonprops = {
                    key: i,
                    tabIndex: index === i ? 0 : -1,
                    "aria-hidden": i !== index,
                };
                //代表已经允许mount加载了: load=true的儿子才能算正常DOM。load=false儿子说明未能够mount进来。允许很长队列只需要加载一部分儿子。
                const load = !lazyLoad || index === i || loaded.has(i);

               return(
                   <animated.div  className="Gesturepanel"  key={i}
                          style={{
                              display,
                              x ,
                              width: "100%",
                              position: "absolute",
                              height: "100%",
                              touchAction: 'none',
                              overflowY: 'auto',
                            }}
                           { ...bind() }
                       >
                         <animated.div  className="Page_pane"
                                style={{
                                    scale,
                                    touchAction: 'none',
                                    width: "100%",
                                    height: "100%",
                                    // boxShadow: `0 62.5px 125px -25px rgba(50, 50, 73, 0.5), 0 37.5px 75px -37.5px rgba(0, 0, 0, 0.6)`,
                                    display: "flex",
                                    flexDirection: "column",
                                    alignSelf: "stretch",
                                    justifyContent: "flex-start",
                                    flexShrink: 0,
                                    overflow: "auto",
                                    // outline: "none",
                                    ...paneStyle
                                }}
                           {...sonprops}
                         >
                            {load && child}
                         </animated.div>
                    </animated.div>
               );
            })}
        </div>
    );
}
);


/*更为底层API使用例子 参考@use-gesture/react包文档
  function doDrag(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
            last,  velocity: [vx, vy], canceled, } = state;
        console.log("拉动D",state,"active",active,"号序",indexRef.current,"canceled:",canceled,"快滑",swpx);
        event.stopPropagation();
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    function doDragStart(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
            last,  velocity: [vx, vy], canceled, } = state;
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    function doDragEnd(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
            last,  velocity: [vx, vy], canceled, } = state;
        event.stopPropagation();
        if (!active && !canceled && Math.abs(swpx) > 0) {
            let Old=indexRef.current;
            let childCount=pages.length;
            const idxNew= indexRef.current + (swpx > 0 ? -1 : 1);
            const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;
            indexRef.current = objIndex;
            console.log("拉动End Old",Old,"NEW=",indexRef.current);
            cancel()
        }
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    const bind 1= useGesture(
        {
            onDrag: (state) => doDrag(state),
            onDragStart: (state) => doDragStart(state),
            onDragEnd: (state) => doDragEnd(state),
        },
       {  }
    )
   const bind 2= useDrag((state) => {
        const { active, axis, movement: [mx,], direction: [xDir,] , cancel, event, swipe:[swpx,],
                       tap,first,last,  canceled, type} = state;
         event.stopPropagation();   //不加双层轮播有问题
         const normaltrigs= ( !canceled && (Math.abs(swpx)>0 || Math.abs(mx)>=width*0.1) );
         const trigged= !active && axis==='x' && (normaltrigs );
         if(trigged){
            let Old=indexRef.current;
            const idxNew= indexRef.current + (xDir>0 ? -1 : 1);
            const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;
            if(normaltrigs){
                if(objIndex===Old)
                    onNestDragEnd && onNestDragEnd([xDir,0]);
                else {
                    addIndexToLoaded(objIndex);
                    indexRef.current = objIndex;
                    onRequestChange(objIndex);
                }
            } else {
                if(type==="pointercancel" && !canceled){
                }
            }
           cancel();
         }
         else{
             if(!canceled && axis!=='x') {
                 console.log("被 cancel()",type);
                 cancel();
             }
         }
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    } ,
    {
        enabled: !noBind,
        swipe: {velocity: 0.1},
        filterTaps: true,
    });
* */
